package cn.wzc.crystal.platform.curd;

import cn.wzc.crystal.platform.query.QueryPageResult;
import cn.wzc.crystal.platform.query.QueryService;
import cn.wzc.crystal.platform.support.AbstractEntity;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jdbc.core.JdbcAggregateTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ReflectionUtils;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

public abstract class AbstractCrudService<Dto extends Serializable, Entity extends AbstractEntity> implements CrudService<Dto, Entity> {

	private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(getClass());

	/**
	 * 数据传输对象
	 */
	private final Class<Dto> dtoClass;

	/**
	 * 实体对象
	 */
	private final Class<Entity> entityClass;
	/**
	 * DTO ID 属性
	 */
	private final Method dtoIdGetter;
	/**
	 * 名称
	 */
	private final String serviceName;


	@Autowired
	private JdbcAggregateTemplate aggregateTemplate;

	@Autowired
	private QueryService queryService;

	@SuppressWarnings("unchecked")
	public AbstractCrudService() {
		Type[] types = ((ParameterizedTypeImpl) getClass().getGenericSuperclass()).getActualTypeArguments();
		String dtoClassName = types[0].getTypeName();
		String entityClassName = types[1].getTypeName();

		try {
			this.dtoClass = (Class<Dto>) getClass().getClassLoader().loadClass(dtoClassName);
			this.entityClass = (Class<Entity>) getClass().getClassLoader().loadClass(entityClassName);
			this.dtoIdGetter = ReflectionUtils.findMethod(this.dtoClass, "getId");
			this.serviceName = getClass().getSimpleName();
		} catch (ClassNotFoundException e) {
			String message = String.format("初始化CrudService异常,解析DTO[%s]和Entity[%s]失败,没有找到对应的类,建议检查classpath下的jar包是否完整", dtoClassName, entityClassName);
			throw new CurdSupportException(message, e);
		}

		if (null == this.dtoIdGetter) {
			throw new CurdSupportException("初始化CrudService异常,wuDTO需要有ID属性");
		}

		log.debug("初始化CrudService完成,使用Dto[{}],Entity[{}]", this.dtoClass.getName(), this.entityClass.getName());
	}

	@Override
	public QueryPageResult<Dto> queryPage(Integer page, Integer size, Map<String, Object> criteria) {
		final String queryName = this.serviceName + ".query";
		return queryService.queryPage(queryName, criteria, page, size, this.dtoClass);
	}

	@Override
	public List<Dto> listAll(Map<String, Object> criteria) {
		final String queryName = this.serviceName + ".query";
		return queryService.queryList(queryName, criteria, this.dtoClass);
	}

	@Override
	public Dto get(Long id) {
		Entity entity = aggregateTemplate.findById(id, this.entityClass);
		return toDto(entity);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Dto save(Dto dto) throws DataNotFoundException {
		final Long id = getDtoId(dto);
		// 更新操作
		if (null != id) {
			return this.update(id, dto);
		} else {
			return this.create(dto);
		}
	}

	@Override
	public Dto create(Dto dto) {
		Entity entity = this.toEntity(dto);
		aggregateTemplate.insert(entity);
		return toDto(entity);
	}

	@Transactional(rollbackFor = Exception.class)
	public Dto update(Long id, Dto dto) throws DataNotFoundException {
		final Entity entity = aggregateTemplate.findById(id, this.entityClass);
		if (null == entity) {
			throw new DataNotFoundException();
		}
		BeanUtils.copyProperties(dto, entity);
		entity.setId(id);
		aggregateTemplate.update(entity);
		return toDto(entity);
	}


	@Override
	@Transactional(rollbackFor = Exception.class)
	public void deleteAll(Long... ids) {
		for (Long id : ids) {
			aggregateTemplate.deleteById(id, this.entityClass);
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void delete(Long id) {
		aggregateTemplate.deleteById(id, this.entityClass);
	}

	@Override
	public Entity toEntity(Dto dto) {
		try {
			final Entity entity = entityClass.newInstance();
			BeanUtils.copyProperties(dto, entity);
			return entity;
		} catch (InstantiationException | IllegalAccessException e) {
			throw new CurdSupportException("无法初始化Entity对象 " + entityClass.getName(), e);
		}
	}

	@Override
	public Dto toDto(Entity entity) {
		try {
			final Dto dto = dtoClass.newInstance();
			BeanUtils.copyProperties(entity, dto);
			return dto;
		} catch (InstantiationException | IllegalAccessException e) {
			throw new CurdSupportException("无法初始化Dto对象 " + dtoClass.getName(), e);
		}
	}

	/**
	 * 获取数据传输对象的ID值
	 *
	 * @param dto 数据传输对象
	 * @return ID值
	 */
	private Long getDtoId(Dto dto) {
		if (dtoIdGetter != null) {
			Object idValue = ReflectionUtils.invokeMethod(dtoIdGetter, dto);
			if (idValue instanceof Long) {
				return (Long) idValue;
			}
		}
		return null;
	}

}
